import React, { ChangeEvent, useRef, useState } from 'react';
import axios from 'axios'
import Button from '../button'
import UploadList from './uploadList'
import Dragger from './dragger'


export interface UploadProps {
    action: string,
    //这个回调函数用于 在上传之前处理一下名字或格式或检查一下大小之类的
    beforeUpload?: (file: File) => boolean | Promise<File>
    // 进度小于100时触发
    onProgress?: (percentage: number, file: File) => void
    onSuccess?: (data: any, file: File) => void
    onError?: (err: any, file: File) => void
    onChange?: (file: File) => void
    // 一开始用户可能传入需要上传的数据 --用于展示
    defaultFileList?: UploadFile[];
    onRemove?: (file: UploadFile) => void
    // 扩展 
    headers?: { [key: string]: any }, //自定义响应头
    name?: string; //表单上传自定义name
    data?: { [key: string]: any } //上传自定义data
    withCredentials?: boolean //是否上传带上cook
    accept?: string;//限制上传类型 .png  input自带 
    multiple?: boolean; //是否支持多文件上传 input自带
    drag?: boolean; //是否支持拖拽上传
}

export type UploadFileStatus = 'ready' | 'uploading' | 'success' | 'error'
//上传文件相关状态的属性 --- 添加在状态里面 
// 开始上传--更新数据
export interface UploadFile {
    uid: string;
    size: number;
    name: string;
    status?: UploadFileStatus;
    percent?: number;
    raw?: File; //原始文件信息 
    response?: any;
    error?: any;
}

const Upload: React.FC<UploadProps> = (props) => {
    // props
    const { action, onProgress, onSuccess, onError, beforeUpload, onChange, onRemove, defaultFileList,
        headers, name, data, withCredentials, accept, multiple, drag, children } = props
    // state -ref
    const inputRef = useRef<HTMLInputElement>(null)
    const [fileList, setFileList] = useState<UploadFile[]>(defaultFileList || [])
    // methods
    const handleBtnClick = () => {
        if (inputRef.current) {
            inputRef.current.click()
        }
    }
    const handleFile = (e: ChangeEvent<HTMLInputElement>) => {
        const files = e.target.files
        if (!files) return
        uploadFiles(files)
        if (inputRef.current) {
            inputRef.current.value = ''
        }
    }
    // 触发选择文件
    const uploadFiles = (files: FileList) => {
        let postFiles = Array.from(files)
        postFiles.forEach(file => {
            // 没有传回调函数就不管了直接往下执行 
            if (!beforeUpload) {
                post(file)
            } else {
                // 传了的情况 
                const result = beforeUpload(file) //拿到处理后的结果 ---有两种情况：布尔 或 promise文件
                if (result && result instanceof Promise) {
                    result.then(processedFile => {
                        post(processedFile)
                    })
                } else if (result !== false) { //这种就是处理文件大小 大于好多kb就外面弹窗不让里面执行
                    post(file)
                }
            }
        })
    }
    // 处理请求
    const post = (file: File) => {
        // 更新fileList数据 ----这是为了拿到每个阶段的状态 用来显示用的 
        let _file: UploadFile = {
            uid: Date.now() + 'upload-file',
            status: 'ready',
            name: file.name,
            size: file.size,
            percent: 0,
            raw: file
        }
        // setFileList([_file, ...fileList])
        setFileList(prevList => {
            return [_file, ...prevList]
        })
        //通过FormData构造函数创建一个空对象
        const formData = new FormData() //怎么搞出了一个map类型
        //可以通过append()方法来追加数据
        // formData.append(file.name, file) 这样名字写死了 
        formData.append(name || 'file', file)
        //通过get方法对值进行读取--set方法对值进行设置console.log(formData.get(file.name,));
        if (data) {
            Object.keys(data).forEach(key => {
                formData.append(key, data[key])
            })
        }
        // 发送请求
        axios.post(action, formData, {
            headers: {
                'Content-Type': 'multipart/form-data',
                ...headers
            },
            withCredentials,//axios自带属性
            onUploadProgress: (e) => { //axios自带的
                let percentage = Math.round((e.loaded * 100) / e.total) || 0;
                if (percentage < 100) {
                    updateFileList(_file, { percent: percentage, status: 'uploading' })
                    // 触发回调函数--生命周期函数 
                    onProgress && onProgress(percentage, file)
                }
            }
        }).then(res => {
            updateFileList(_file, { status: 'success', response: res.data })
            onSuccess && onSuccess(res.data, file)
            onChange && onChange(file)
        }).catch(err => {
            updateFileList(_file, { status: 'error', error: err })
            onError && onError(err, file)
            onChange && onChange(file)
        })
    }
    // 更新FileList 也就是更新UploadFile里面的状态
    const updateFileList = (needUpdateFile: UploadFile, updateObj: Partial<UploadFile>) => {
        // 这里是useState是异步的，我们如何拿到上一次的值 --传递函数的方式
        setFileList(prevList => {
            // 把新更新的文件里面对应的信息，更新到原有的文件列表对应文件里面去
            return prevList.map(file => {
                if (file.uid === needUpdateFile.uid) {
                    return { ...file, ...updateObj }
                } else {
                    return file
                }
            })
        })
    }
    // 删除上传文件的列表 
    const remove = (file: UploadFile) => {
        setFileList((prevList) => {
            return prevList.filter(item => item.uid !== file.uid)
        })
        onRemove && onRemove(file)
    }
    return (
        <div className='viking-upload-component'>
            {/* 下面的可以外面传入进去  button等价 children */}
            {/* {drag ? <Dragger onFile={(files) => { uploadFiles(files) }} /> : children} */}
            {drag ? <Dragger onFile={(files) => { uploadFiles(files) }}>
                {children}
            </Dragger> :
                <Button btnType='primary' onClick={handleBtnClick}>Upload File</Button>}

            <input className='viking-file-input' style={{ display: 'none' }}
                type='file' ref={inputRef} onChange={handleFile} accept={accept} multiple={multiple} />
            <UploadList fileList={fileList} onRemove={remove}></UploadList>
        </div>
    )
}

Upload.defaultProps = {
    name: 'file'
}
export default Upload;